﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Oxage.Controls
{
	public class DualAxisController : Control
	{
		#region Constructor
		public DualAxisController()
		{
			this.DefaultStyleKey = typeof(DualAxisController);
		}
		#endregion

		#region Template
		protected FrameworkElement Area;
		protected FrameworkElement Nipple;

		public override void OnApplyTemplate()
		{
			this.Area = GetTemplateChild("Area") as FrameworkElement;
			this.Nipple = GetTemplateChild("Nipple") as FrameworkElement;

			this.MouseLeave += (sender, e) =>
			{
				if (this.IsMouseEnabled)
				{
					this.IsMouseDown = false;
					ResetCursorPosition();
				}
			};
			this.LostMouseCapture += (sender, e) =>
			{
				if (this.IsMouseEnabled)
				{
					this.IsMouseDown = false;
					ResetCursorPosition();
				}
			};
			Area.MouseLeftButtonUp += (sender, e) =>
			{
				if (this.IsMouseEnabled)
				{
					this.IsMouseDown = false;
					ResetCursorPosition();
				}
			};
			Area.MouseLeftButtonDown += (sender, e) =>
			{
				if (this.IsMouseEnabled)
				{
					this.IsMouseDown = true;
					var point = e.GetPosition(this.Area);
					SetCursorPosition(point);
				}
			};
			Area.MouseMove += (sender, e) =>
			{
				if (this.IsMouseEnabled && this.IsMouseDown)
				{
					var point = e.GetPosition(this.Area);
					SetCursorPosition(point);
				}
			};
			Area.Loaded += (sender, e) =>
			{
				ResetCursorPosition();
			};

			base.OnApplyTemplate();
		}
		#endregion

		#region Protected properties
		protected bool IsMouseDown
		{
			get
			{
				return this.isMouseDown;
			}
			set
			{
				this.isMouseDown = value;

				var nipple = this.Nipple as Border;
				if (nipple != null)
				{
					nipple.Background = (value ? new SolidColorBrush(Color.FromArgb(0xFF, 0xBF, 0xFF, 0x00)) : new SolidColorBrush(Color.FromArgb(0xFF, 0x80, 0x80, 0x80)));
				}
			}
		}
		private bool isMouseDown = false;

		protected Point CenterPoint
		{
			get
			{
				var point = new Point()
				{
					X = this.Area.ActualWidth / 2 - 3, //3 for border thickness
					Y = this.Area.ActualWidth / 2 - 3
				};
				return point;
			}
		}
		#endregion

		#region Public methods
		public void ResetCursorPosition()
		{
			this.Position = new Point(0, 0);
		}
		#endregion

		#region Protected methods
		protected void SetCursorPosition(Point point)
		{
			var offset = point;
			offset.X -= this.Nipple.ActualWidth / 2;
			offset.Y -= this.Nipple.ActualHeight / 2;

			this.Nipple.Margin = new Thickness(offset.X, offset.Y, 0, 0);

			var figure = new PathFigure();
			figure.StartPoint = this.CenterPoint;
			figure.Segments.Add(new LineSegment() { Point = point });
			var geometry = new PathGeometry();
			geometry.Figures.Add(figure);
			this.SpringGeometry = geometry;
		}
		#endregion

		#region Dependency properties

		#region SpringGeometry
		public PathGeometry SpringGeometry
		{
			get { return (PathGeometry)GetValue(SpringGeometryProperty); }
			protected set { SetValue(SpringGeometryProperty, value); }
		}

		public static readonly DependencyProperty SpringGeometryProperty = DependencyProperty.Register("SpringGeometry", typeof(PathGeometry), typeof(DualAxisController), new PropertyMetadata(SpringGeometryChanged));

		private static void SpringGeometryChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
		}
		#endregion

		#region IsMouseEnabled
		public bool IsMouseEnabled
		{
			get { return (bool)GetValue(IsMouseEnabledProperty); }
			set { SetValue(IsMouseEnabledProperty, value); }
		}

		public static readonly DependencyProperty IsMouseEnabledProperty = DependencyProperty.Register("IsMouseEnabled", typeof(bool), typeof(DualAxisController), new PropertyMetadata(IsMouseEnabledChanged));

		private static void IsMouseEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
		}
		#endregion

		#region Position
		/// <summary>
		/// Gets or sets relative position of cursor. Values -1 &lt; x &lt; 1 and -1 &lt; y &lt; 1.
		/// </summary>
		public Point Position
		{
			get { return (Point)GetValue(PositionProperty); }
			set { SetValue(PositionProperty, value); }
		}

		public static readonly DependencyProperty PositionProperty = DependencyProperty.Register("Position", typeof(Point), typeof(DualAxisController), new PropertyMetadata(PositionChanged));

		private static void PositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			var control = d as DualAxisController;
			if (control != null && control.Area != null)
			{
				Point p = (Point)e.NewValue;

				//double k = control.Area.ActualWidth / 2 - 3; //3 for border thickness
				double k = control.Area.ActualWidth / 2;
				double x = 0, y = 0;

				//Bounds
				if (p.X < -1) p.X = -1;
				if (p.X > 1) p.X = 1;
				if (p.Y < -1) p.Y = -1;
				if (p.Y > 1) p.Y = 1;

				//Calculate absolute positions
				if (p.X < 0) x = k * (1 + Math.Abs(p.X));
				else x = k * (1 - Math.Abs(p.X));
				if (p.Y < 0) y = k * (1 + Math.Abs(p.Y));
				else y = k * (1 - Math.Abs(p.Y));

				Point point = new Point(x, y);
				control.SetCursorPosition(point);
			}
		}
		#endregion

		#endregion
	}
}
